package mybatisPlugin.config;


import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.util.JdbcConstants;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import mybatisPlugin.entity.SqlInfo;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.util.StringUtils;

import java.sql.Connection;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * LogServiceInterceptor
 *
 * @author 方典典
 * @date 2022/7/20 17:57
 */
@Intercepts(
        {
//                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
//                @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
//                @Signature(type = StatementHandler.class, method = "query", args = {Statement.class, ResultHandler.class})
                @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})

        }
)
public class LogServiceInterceptor implements Interceptor {

    private static final String SELECT = "select ";
    private static final String WHERE = "where";
    private static final String SPACE = " ";
    private static final String FROM = "from";
    private static final String COMMA = ",";
    private static final String BUSINESS_LOG_LEVEL = "business";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
//        Object[] args = invocation.getArgs();
//        MappedStatement ms = (MappedStatement) args[0];
//        Object parameter = args[1];
//        RowBounds rowBounds = (RowBounds) args[2];
//        ResultHandler resultHandler = (ResultHandler) args[3];
//        Executor executor = (Executor) invocation.getTarget();
//        CacheKey cacheKey;
//        BoundSql boundSql;




//        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget());
        // 2.获取MetaObject
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
        if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) {
            return invocation.proceed();
        }
        
//        Statement statement = (Statement)invocation.getArgs()[0];

        boolean isUpdateSql = false;
        try {
            isUpdateSql = SqlCommandType.UPDATE.equals((SystemMetaObject.forObject(statementHandler.getParameterHandler()).getValue("sqlCommandType")));
        } catch (Exception e) {

        }

        SqlInfo sqlInfo;
        try {
            sqlInfo = convertSQL(statementHandler);
        } catch (Throwable e) {
            // 解析失败直接返回
            return invocation.proceed();
        }

        Object value = metaObject.getValue("delegate.boundSql.sql");
        String sql = sqlInfo.getSql();

        metaObject.setValue("delegate.boundSql.sql", sql + "  and 1 = 1");



        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }



    /**
     * 将update转成select
     *
     * @param sqlInfo
     * @return java.lang.String
     * @author 方典典
     * @date 2020/8/24 10:23
     */
    public String getSelectSQL(SqlInfo sqlInfo) {
        Map<String, String> items = sqlInfo.getItems();
        StringBuffer sql = new StringBuffer(SELECT);
        String column = StringUtils.arrayToDelimitedString(items.keySet().toArray(), COMMA);
        sql.append(column);
        sql.append(SPACE);
        sql.append(FROM);
        sql.append(SPACE);
        sql.append(StringUtils.arrayToDelimitedString(sqlInfo.getTables().toArray(), COMMA));
        sql.append(SPACE);
        sql.append(WHERE);
        sql.append(SPACE);
        sql.append(sqlInfo.getWhere());
        return SQLUtils.format(sql.toString(), JdbcConstants.MYSQL);
    }

    /**
     * 解析SQL
     *
     * @param statementHandler
     * @return java.util.Map<java.lang.String, java.lang.Object>
     * @author 方典典
     * @date 2020/8/24 10:24
     */
    public static SqlInfo convertSQL(StatementHandler statementHandler) throws Throwable {
        BoundSql boundSql = statementHandler.getBoundSql();
        String sql = boundSql.getSql();
        Object parameterObject = statementHandler.getParameterHandler().getParameterObject();
        // 目标sql的所有参数
        List<Object> parameterList = new ArrayList<>();
        if (ReflectObjectUtils.isPrimitive(parameterObject) || parameterObject instanceof String) {
            // 基础数据类型
            parameterList.add(parameterObject);
        } else if (parameterObject instanceof Map) {
            boundSql.getParameterMappings().forEach(parameterMapping -> {
                String parameterName = parameterMapping.getProperty();
                if (((Map) parameterObject).containsKey(parameterName)) {
                    parameterList.add(((Map) parameterObject).get(parameterMapping.getProperty()));
                } else if (boundSql.hasAdditionalParameter(parameterName)) {
                    parameterList.add(boundSql.getAdditionalParameter(parameterName));
                }
            });
        } else {
            Map<String, Object> parametersMap = ReflectObjectUtils.getKeyAndValue(parameterObject);
            boundSql.getParameterMappings().forEach(parameterMapping -> {
                if (parametersMap.containsKey(parameterMapping.getProperty())) {
                    parameterList.add(parametersMap.get(parameterMapping.getProperty()));
                }
            });
        }
       // sql = SQLUtils.format(sql, JdbcConstants.MYSQL, parameterList);
        List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, JdbcConstants.MYSQL);
        CustomizeSqlASTVisitor visitor = new CustomizeSqlASTVisitor();
        for (SQLStatement stmt : stmtList) {
            stmt.accept(visitor);
        }
        return visitor.getSqlInfo();
    }




}
